import com.pi4j.io.gpio.*;
import com.pi4j.io.i2c.I2CBus;
import com.pi4j.io.i2c.I2CDevice;
import com.pi4j.io.i2c.I2CFactory;
import com.pi4j.wiringpi.Gpio;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Objects;

@Slf4j
public class RaspberrySensor {

    private static final GpioController GPIO_CONTROLLER = GpioFactory.getInstance();

    public static final GpioPinDigitalOutput LED_GREEN =
            GPIO_CONTROLLER.provisionDigitalOutputPin(RaspiPin.GPIO_00, "LED_GREEN", PinState.HIGH);

    public static final GpioPinDigitalOutput LED_ORANGE =
            GPIO_CONTROLLER.provisionDigitalOutputPin(RaspiPin.GPIO_02, "LED_ORANGE", PinState.HIGH);

    public static final GpioPinDigitalOutput MOTOR =
            GPIO_CONTROLLER.provisionDigitalOutputPin(RaspiPin.GPIO_03, "MOTOR", PinState.LOW);

    public static final GpioPinDigitalInput INFRARED =
            GPIO_CONTROLLER.provisionDigitalInputPin(RaspiPin.GPIO_04, "INFRARED", PinPullResistance.PULL_DOWN);

    public static final GpioPinDigitalInput SOUND =
            GPIO_CONTROLLER.provisionDigitalInputPin(RaspiPin.GPIO_05, "SOUND", PinPullResistance.PULL_DOWN);

    public static final GpioPinDigitalInput MQ7_DIGITAL_INPUT =
            GPIO_CONTROLLER.provisionDigitalInputPin(RaspiPin.GPIO_26, "MQ7", PinPullResistance.PULL_DOWN);

    public static final GpioPinDigitalInput LIGHT_DIGITAL_INPUT =
            GPIO_CONTROLLER.provisionDigitalInputPin(RaspiPin.GPIO_27, "LIGHT", PinPullResistance.PULL_DOWN);

    public static final GpioPinDigitalInput FLAME_DIGITAL_INPUT =
            GPIO_CONTROLLER.provisionDigitalInputPin(RaspiPin.GPIO_28, "FLAME", PinPullResistance.PULL_DOWN);

    public static final GpioPinDigitalInput MQ4_DIGITAL_INPUT =
            GPIO_CONTROLLER.provisionDigitalInputPin(RaspiPin.GPIO_29, "MQ4", PinPullResistance.PULL_DOWN);


    public static void ledGreenLightOn() {
        ledLightOn(LED_GREEN);
        log.info("LED_GREEN is on.");
    }

    public static void ledOrangeLightOn() {
        ledLightOn(LED_ORANGE);
        log.info("LED_ORANGE is on.");
    }

    public static void ledGreenLightOff() {
        ledLightOff(LED_GREEN);
        log.info("LED_GREEN is off.");
    }

    public static void ledOrangeLightOff() {
        ledLightOff(LED_ORANGE);
        log.info("LED_ORANGE is off.");
    }

    private static void ledLightOn(GpioPinDigitalOutput gpioPinDigitalOutput) {
        gpioPinDigitalOutput.low();
    }

    private static void ledLightOff(GpioPinDigitalOutput gpioPinDigitalOutput) {
        gpioPinDigitalOutput.high();
    }

    public static class FanThread implements Runnable {

        public static volatile boolean running = true;

        private ControlService.MessageBody messageBody;

        public void refreshMessageBody(ControlService.MessageBody messageBody) {
            this.messageBody = messageBody;
        }

        public static void stop() {
            running = false;
        }

        @Override
        public void run() {
            int pwm = messageBody.getFans();
            if (pwm < 0 || pwm > 10) {
                log.error("fan pwm must be in range of 0~10.");
            } else if (pwm == 0) {
                log.info("fan Stop.");
                fanStop();
            } else {
                running = true;
                try {
                    while (running) {
                        MOTOR.high();
                        Thread.sleep(pwm);
                        MOTOR.low();
                        Thread.sleep(10 - pwm);
                    }
                } catch (InterruptedException e) {
                    log.error("fan run error: ", e);
                    Thread.currentThread().interrupt(); // 恢复中断状态
                }
            }
        }
    }

    public static void fanStop() {
        FanThread.stop();
        MOTOR.low();
    }

    /**
     * 11：DHT11 传感器型号。
     * 25：连接传感器的 GPIO 引脚编号（BCM 编号）
     */
    public static final String SHELL = "python3 /home/pi/liyang/Adafruit_Python_DHT/examples/AdafruitDHT.py 11 25";

    public static SuperviseService.Dht11 temperatureAndHumidity() {
        try {
            Process process = Runtime.getRuntime().exec(SHELL);
            int exitValue = process.waitFor();
            if (0 != exitValue) {
                log.info("fail to run temperature and humidity sensor data collection, exit code :" + exitValue);
                return null;
            }
            SuperviseService.Dht11 dht11 = new SuperviseService.Dht11();
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                String[] spaceSplitArray = line.split(" {2}");

                String[] tempArr = spaceSplitArray[0].split("=");
                if (tempArr.length == 2) {
                    log.info("Temp=" + tempArr[1]);
                    dht11.setTemperature(Double.valueOf(tempArr[1].substring(0, tempArr[1].length() - 1)));
                    System.out.println("Temp=" + tempArr[1]);
                }
                String[] humidityArr = spaceSplitArray[1].split("=");
                if (humidityArr.length == 2) {
                    log.info("Humidity=" + humidityArr[1]);
                    dht11.setHumidity(Double.valueOf(humidityArr[1].substring(0, humidityArr[1].length() - 1)));
                    System.out.println("Humidity=" + humidityArr[1]);


                }
            }
            return dht11;
        } catch (Exception e) {
            log.error("Error happened while running temperature and humidity sensor data collection:", e);
            return null;
        }
    }

    private static final int BUZZER_PIN = 1; //物理引脚12,BCM编号GPIO 18,GPIO_01

    // 音符频率映射表（单位：Hz）
    private static final int NOTE_C4 = 262;
    private static final int NOTE_D4 = 294;
    private static final int NOTE_E4 = 330;
    private static final int NOTE_F4 = 349;
    private static final int NOTE_G4 = 392;
    private static final int NOTE_A4 = 440;
    private static final int NOTE_B4 = 494;

    public static void buzzer(int times) {
        if (times == 0) {
            buzzerStop();
            return;
        }
        try {
            // 配置PWM（直接操作WiringPi底层）
//            Gpio.wiringPiSetupGpio(); // 初始化WiringPi
            Gpio.pinMode(BUZZER_PIN, Gpio.PWM_OUTPUT);
            Gpio.pwmSetMode(Gpio.PWM_MODE_MS);  // 必须使用Mark-Space模式
            Gpio.pwmSetRange(1024);             // 范围固定为1024
            Gpio.pwmSetClock(500);              // 时钟分频

            for (int i = 0; i < times; i++) {
                // 播放《欢乐颂》片段

                playNote(NOTE_E4, 500);
                playNote(NOTE_F4, 500);
                playNote(NOTE_G4, 500);
                playNote(NOTE_G4, 500);
                playNote(NOTE_F4, 500);
                playNote(NOTE_E4, 500);
                playNote(NOTE_D4, 1000);
                log.info("buzzer buzzing ({}).", i);
            }
            // 清理资源
            buzzerStop();
        } catch (InterruptedException e) {
            log.error("buzzer run error: ", e);
        }
    }

    public static void buzzerStop() {
        Gpio.pwmWrite(BUZZER_PIN, 0);
        log.info("buzzer stop.");
    }

    private static void playNote(int frequency, int duration) throws InterruptedException {
        if (frequency == 0) {
            Thread.sleep(duration);
            return;
        }
        // 计算PWM值（公式需匹配v1.3的硬件特性）
        int pwmValue = frequency * 1024 / 384;
        Gpio.pwmWrite(BUZZER_PIN, pwmValue);
        Thread.sleep(duration);
        // 停止发声
        Gpio.pwmWrite(BUZZER_PIN, 0);
        Thread.sleep(50); // 音符间隔
    }

    // 半步控制序列 (4相8拍)
    public static final byte[][] STEP_SEQUENCE = {
            {1, 0, 0, 0},  // Step 1
            {1, 1, 0, 0},  // Step 2
            {0, 1, 0, 0},  // Step 3
            {0, 1, 1, 0},  // Step 4
            {0, 0, 1, 0},  // Step 5
            {0, 0, 1, 1},  // Step 6
            {0, 0, 0, 1},  // Step 7
            {1, 0, 0, 1}   // Step 8
    };

    /**
     * ULN2003A驱动28YBJ-48步进电机控制
     * 硬件连接：
     * IN1 -> GPIO_21
     * IN2 -> GPIO_22
     * IN3 -> GPIO_23
     * IN4 -> GPIO_24
     */
    public static final GpioPinDigitalOutput[] STEP_MOTOR_PIN_ARRAY = new GpioPinDigitalOutput[]{
            GPIO_CONTROLLER.provisionDigitalOutputPin(RaspiPin.GPIO_21, "STEP_MOTOR_1", PinState.LOW),
            GPIO_CONTROLLER.provisionDigitalOutputPin(RaspiPin.GPIO_22, "STEP_MOTOR_2", PinState.LOW),
            GPIO_CONTROLLER.provisionDigitalOutputPin(RaspiPin.GPIO_23, "STEP_MOTOR_3", PinState.LOW),
            GPIO_CONTROLLER.provisionDigitalOutputPin(RaspiPin.GPIO_24, "STEP_MOTOR_4", PinState.LOW)};

    static {
        for (int i = 0; i < 4; i++) {
            STEP_MOTOR_PIN_ARRAY[i].setShutdownOptions(true, PinState.LOW);
        }
        Runtime.getRuntime().addShutdownHook(new Thread(RaspberrySensor::shutdown));
    }

    public static boolean clockwise = true;

    /**
     * 设置旋转方向
     *
     * @param clockwise true=顺时针, false=逆时针
     */
    public static void setDirection(boolean clockwise) {
        RaspberrySensor.clockwise = clockwise;
        log.info("step motor current direction is {}", clockwise ? "clockwise" : "counterclockwise");
    }

    public static int stepDelay = 5;

    /**
     * 设置转速 (RPM)
     *
     * @param rpm 每分钟转数
     */
    public static void setSpeed(double rpm) {
        RaspberrySensor.stepDelay = (int) (60000 / (4096 * rpm));
        log.info("step motor current speed is {} rpm", rpm);
    }

    /**
     * 旋转指定步数
     *
     * @param circle 圈数 (4096步=1圈)
     */
    public static void rotate(double circle) throws InterruptedException {
        double steps = circle * 4096;
        log.info(String.format("step motor rotate %.1f circle", circle));
        for (int i = 0; i < steps; i++) {
            singleStep();
            Thread.sleep(stepDelay);
        }
        stepMotorStop(); // 完成后断电
    }

    public static int currentStep = 0;

    /**
     * 执行单步操作
     */
    public static void singleStep() {
        // 更新步序索引
        if (clockwise) {
            currentStep = (currentStep + 1) % STEP_SEQUENCE.length;
        } else {
            currentStep = (currentStep - 1 + STEP_SEQUENCE.length) % STEP_SEQUENCE.length;
        }
        // 设置引脚状态
        for (int i = 0; i < 4; i++) {
            STEP_MOTOR_PIN_ARRAY[i].setState(STEP_SEQUENCE[currentStep][i] == 1);
        }
    }

    /**
     * 停止电机 (关闭所有线圈)
     */
    public static void stepMotorStop() {
        for (GpioPinDigitalOutput pin : STEP_MOTOR_PIN_ARRAY) {
            pin.low();
        }
        log.info("step motor stop.");
    }

    /**
     * 安全关闭GPIO资源
     */
    public static void shutdown() {
        ledGreenLightOff();
        ledOrangeLightOff();
        fanStop();
        buzzerStop();
        stepMotorStop();
        GPIO_CONTROLLER.shutdown();
    }

    public static void stepMotorRun(ControlService.SteppingMotor steppingMotor) {
        if (Objects.isNull(steppingMotor)) {
            return;
        }
        log.info("step motor run: {}", JacksonUtil.serialize(steppingMotor));
        stepMotorRun(steppingMotor.isClockwise(), steppingMotor.getRpm(), steppingMotor.getCircle());
    }

    public static void stepMotorRun(boolean clockwise, double rpm, double circle) {
        if (JacksonUtil.isZero(rpm) || JacksonUtil.isZero(circle)) {
            return;
        }
        try {
            setDirection(clockwise);
            setSpeed(rpm);
            rotate(circle);
        } catch (Exception e) {
            log.error(" stepMotorRun error:", e);
        }
    }

    public static final int I2C_BUS = 1;// 定义I2C总线和设备地址

    public static final int PCF8591_ADDRESS = 0x48; // 默认地址

    public static I2CDevice PCF8591;// 创建I2C设备

    static {
        try {
            I2CBus bus = I2CFactory.getInstance(I2C_BUS);
            PCF8591 = bus.getDevice(PCF8591_ADDRESS);
        } catch (IOException | I2CFactory.UnsupportedBusNumberException e) {
            log.error("I2C device init error:", e);
        }
    }

    public static SuperviseService.SensorValue infrared() {
        return new SuperviseService.SensorValue(null, INFRARED.isLow());
    }

    public static SuperviseService.SensorValue sound() {
        return new SuperviseService.SensorValue(null, SOUND.isLow());
    }

    public static SuperviseService.SensorValue mq7() {
        try {
            int value = readPCF8591(0);
            log.info("mq7 sensor channel: 0, value: " + value);
            return new SuperviseService.SensorValue(value, MQ7_DIGITAL_INPUT.isLow());
        } catch (IOException e) {
            log.error("mq7 sensor read error:", e);
            return null;
        }
    }

    public static SuperviseService.SensorValue light() {
        try {
            int value = readPCF8591(1);
            log.info("light sensor channel: 1, value: " + value);
            return new SuperviseService.SensorValue(value, LIGHT_DIGITAL_INPUT.isLow());
        } catch (IOException e) {
            log.error("light sensor read error:", e);
            return null;
        }
    }

    public static SuperviseService.SensorValue flame() {
        try {
            int value = readPCF8591(2);
            log.info("flame sensor channel: 2, value: " + value);
            return new SuperviseService.SensorValue(value, FLAME_DIGITAL_INPUT.isLow());
        } catch (IOException e) {
            log.error("flame sensor read error:", e);
            return null;
        }
    }

    public static SuperviseService.SensorValue mq4() {
        try {
            int value = readPCF8591(3);
            log.info("mq4 sensor channel: 3, value: " + value);
            return new SuperviseService.SensorValue(value, MQ4_DIGITAL_INPUT.isLow());
        } catch (IOException e) {
            log.error("mq4 sensor read error:", e);
            return null;
        }
    }

    /**
     * 读取PCF8591 ADC值的方法
     *
     * @param channel 通道
     */
    private static int readPCF8591(int channel) throws IOException {
        byte controlByte = (byte) (0x40 | (channel & 0x03));// 设置控制字节
        PCF8591.write(controlByte);  // 写入控制字节
        byte[] buffer = new byte[2];// 读取ADC值
        PCF8591.read(buffer, 0, 2);
        return buffer[1] & 0xFF;// 返回ADC值
    }

}
